#include "KTaskQueue.h"
#include "KTaskThread.h"
#include <algorithm>

KTaskQueue::KTaskQueue() : m_stopped(true), m_worker(new KTaskThread)
{
    m_worker->append(std::bind(&KTaskQueue::_worker, this));
}

KTaskQueue::~KTaskQueue()
{
    stop();
    assert(m_tasks.empty());
}

void KTaskQueue::start()
{
    if (m_stopped) {
        m_stopped = false;
        m_worker->start();
    }
}

void KTaskQueue::stop()
{
    if (!m_stopped) {
        {
            KLocker lock(*this);
            m_stopped = true;
        }
        m_newTask.set();
        m_worker->join();
    }
}

bool KTaskQueue::submit(KTask &&task)
{
    // Double-checked locking
    if (task.isValid() && !m_stopped) {
        KLocker lock(*this);
        if (!m_stopped) {
            auto pos = std::lower_bound(m_tasks.cbegin(), m_tasks.cend(), task);
            m_tasks.insert(pos, std::move(task));
            m_newTask.set();
            return true;
        }
    }
    return false;
}

void KTaskQueue::_worker()
{
    while (m_newTask.wait()) {
        doTasks();
        {
            KLocker lock(*this);
            if (m_stopped) break;
        }
    }
    doTasks();
}

void KTaskQueue::doTasks()
{
    KTask task;
    for (;;) {
        {
            KLocker lock(*this);
            if (m_tasks.empty()) {
                break;
            } else {
                task = std::move(m_tasks.back());
                m_tasks.pop_back();
            }
        }
        task.exec();
    }
}